1   /*
2    * Copyright (C) 2011 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.reflect;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.base.Preconditions.checkNotNull;
21  import static com.google.common.collect.Iterables.transform;
22  
23  import com.google.common.annotations.VisibleForTesting;
24  import com.google.common.base.Function;
25  import com.google.common.base.Joiner;
26  import com.google.common.base.Objects;
27  import com.google.common.base.Predicates;
28  import com.google.common.collect.ImmutableList;
29  import com.google.common.collect.Iterables;
30  
31  import java.io.Serializable;
32  import java.lang.reflect.AnnotatedElement;
33  import java.lang.reflect.Array;
34  import java.lang.reflect.GenericArrayType;
35  import java.lang.reflect.GenericDeclaration;
36  import java.lang.reflect.InvocationTargetException;
37  import java.lang.reflect.Method;
38  import java.lang.reflect.ParameterizedType;
39  import java.lang.reflect.Type;
40  import java.lang.reflect.TypeVariable;
41  import java.lang.reflect.WildcardType;
42  import java.util.Arrays;
43  import java.util.Collection;
44  import java.util.concurrent.atomic.AtomicReference;
45  
46  import javax.annotation.Nullable;
47  
48  /**
49   * Utilities for working with {@link Type}.
50   *
51   * @author Ben Yu
52   */
53  final class Types {
54  
55    /** Class#toString without the "class " and "interface " prefixes */
56    private static final Function<Type, String> TYPE_NAME =
57        new Function<Type, String>() {
58          @Override public String apply(Type from) {
59            return JavaVersion.CURRENT.typeName(from);
60          }
61        };
62  
63    private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null");
64  
65    /** Returns the array type of {@code componentType}. */
66    static Type newArrayType(Type componentType) {
67      if (componentType instanceof WildcardType) {
68        WildcardType wildcard = (WildcardType) componentType;
69        Type[] lowerBounds = wildcard.getLowerBounds();
70        checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds.");
71        if (lowerBounds.length == 1) {
72          return supertypeOf(newArrayType(lowerBounds[0]));
73        } else {
74          Type[] upperBounds = wildcard.getUpperBounds();
75          checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound.");
76          return subtypeOf(newArrayType(upperBounds[0]));
77        }
78      }
79      return JavaVersion.CURRENT.newArrayType(componentType);
80    }
81  
82    /**
83     * Returns a type where {@code rawType} is parameterized by
84     * {@code arguments} and is owned by {@code ownerType}.
85     */
86    static ParameterizedType newParameterizedTypeWithOwner(
87        @Nullable Type ownerType, Class<?> rawType, Type... arguments) {
88      if (ownerType == null) {
89        return newParameterizedType(rawType, arguments);
90      }
91      // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE
92      checkNotNull(arguments);
93      checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType);
94      return new ParameterizedTypeImpl(ownerType, rawType, arguments);
95    }
96  
97    /**
98     * Returns a type where {@code rawType} is parameterized by
99     * {@code arguments}.
100    */
101   static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) {
102       return new ParameterizedTypeImpl(
103           ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments);
104   }
105 
106   /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */
107   private enum ClassOwnership {
108 
109     OWNED_BY_ENCLOSING_CLASS {
110       @Nullable
111       @Override
112       Class<?> getOwnerType(Class<?> rawType) {
113         return rawType.getEnclosingClass();
114       }
115     },
116     LOCAL_CLASS_HAS_NO_OWNER {
117       @Nullable
118       @Override
119       Class<?> getOwnerType(Class<?> rawType) {
120         if (rawType.isLocalClass()) {
121           return null;
122         } else {
123           return rawType.getEnclosingClass();
124         }
125       }
126     };
127 
128     @Nullable abstract Class<?> getOwnerType(Class<?> rawType);
129 
130     static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior();
131 
132     private static ClassOwnership detectJvmBehavior() {
133       class LocalClass<T> {}
134       Class<?> subclass = new LocalClass<String>() {}.getClass();
135       ParameterizedType parameterizedType = (ParameterizedType)
136           subclass.getGenericSuperclass();
137       for (ClassOwnership behavior : ClassOwnership.values()) {
138         if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) {
139           return behavior;
140         }
141       }
142       throw new AssertionError();
143     }
144   }
145 
146   /**
147    * Returns a new {@link TypeVariable} that belongs to {@code declaration} with
148    * {@code name} and {@code bounds}.
149    */
150   static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable(
151       D declaration, String name, Type... bounds) {
152     return new TypeVariableImpl<D>(
153         declaration,
154         name,
155         (bounds.length == 0)
156             ? new Type[] { Object.class }
157             : bounds);
158   }
159 
160   /** Returns a new {@link WildcardType} with {@code upperBound}. */
161   @VisibleForTesting static WildcardType subtypeOf(Type upperBound) {
162     return new WildcardTypeImpl(new Type[0], new Type[] { upperBound });
163   }
164 
165   /** Returns a new {@link WildcardType} with {@code lowerBound}. */
166   @VisibleForTesting static WildcardType supertypeOf(Type lowerBound) {
167     return new WildcardTypeImpl(new Type[] { lowerBound }, new Type[] { Object.class });
168   }
169 
170   /**
171    * Returns human readable string representation of {@code type}.
172    * <ul>
173    * <li> For array type {@code Foo[]}, {@code "com.mypackage.Foo[]"} are
174    * returned.
175    * <li> For any class, {@code theClass.getName()} are returned.
176    * <li> For all other types, {@code type.toString()} are returned.
177    * </ul>
178    */
179   static String toString(Type type) {
180     return (type instanceof Class)
181         ? ((Class<?>) type).getName()
182         : type.toString();
183   }
184 
185   @Nullable static Type getComponentType(Type type) {
186     checkNotNull(type);
187     final AtomicReference<Type> result = new AtomicReference<Type>();
188     new TypeVisitor() {
189       @Override void visitTypeVariable(TypeVariable<?> t) {
190         result.set(subtypeOfComponentType(t.getBounds()));
191       }
192       @Override void visitWildcardType(WildcardType t) {
193         result.set(subtypeOfComponentType(t.getUpperBounds()));
194       }
195       @Override void visitGenericArrayType(GenericArrayType t) {
196         result.set(t.getGenericComponentType());
197       }
198       @Override void visitClass(Class<?> t) {
199         result.set(t.getComponentType());
200       }
201     }.visit(type);
202     return result.get();
203   }
204 
205   /**
206    * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null
207    * otherwise.
208    */
209   @Nullable private static Type subtypeOfComponentType(Type[] bounds) {
210     for (Type bound : bounds) {
211       Type componentType = getComponentType(bound);
212       if (componentType != null) {
213         // Only the first bound can be a class or array.
214         // Bounds after the first can only be interfaces.
215         if (componentType instanceof Class) {
216           Class<?> componentClass = (Class<?>) componentType;
217           if (componentClass.isPrimitive()) {
218             return componentClass;
219           }
220         }
221         return subtypeOf(componentType);
222       }
223     }
224     return null;
225   }
226 
227   private static final class GenericArrayTypeImpl
228       implements GenericArrayType, Serializable {
229 
230     private final Type componentType;
231 
232     GenericArrayTypeImpl(Type componentType) {
233       this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType);
234     }
235 
236     @Override public Type getGenericComponentType() {
237       return componentType;
238     }
239 
240     @Override public String toString() {
241       return Types.toString(componentType) + "[]";
242     }
243 
244     @Override public int hashCode() {
245       return componentType.hashCode();
246     }
247 
248     @Override public boolean equals(Object obj) {
249       if (obj instanceof GenericArrayType) {
250         GenericArrayType that = (GenericArrayType) obj;
251         return Objects.equal(
252             getGenericComponentType(), that.getGenericComponentType());
253       }
254       return false;
255     }
256 
257     private static final long serialVersionUID = 0;
258   }
259 
260   private static final class ParameterizedTypeImpl
261       implements ParameterizedType, Serializable {
262 
263     private final Type ownerType;
264     private final ImmutableList<Type> argumentsList;
265     private final Class<?> rawType;
266 
267     ParameterizedTypeImpl(
268         @Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) {
269       checkNotNull(rawType);
270       checkArgument(typeArguments.length == rawType.getTypeParameters().length);
271       disallowPrimitiveType(typeArguments, "type parameter");
272       this.ownerType = ownerType;
273       this.rawType = rawType;
274       this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments);
275     }
276 
277     @Override public Type[] getActualTypeArguments() {
278       return toArray(argumentsList);
279     }
280 
281     @Override public Type getRawType() {
282       return rawType;
283     }
284 
285     @Override public Type getOwnerType() {
286       return ownerType;
287     }
288 
289     @Override public String toString() {
290       StringBuilder builder = new StringBuilder();
291       if (ownerType != null) {
292         builder.append(JavaVersion.CURRENT.typeName(ownerType)).append('.');
293       }
294       builder.append(rawType.getName())
295           .append('<')
296           .append(COMMA_JOINER.join(transform(argumentsList, TYPE_NAME)))
297           .append('>');
298       return builder.toString();
299     }
300 
301     @Override public int hashCode() {
302       return (ownerType == null ? 0 : ownerType.hashCode())
303           ^ argumentsList.hashCode() ^ rawType.hashCode();
304     }
305 
306     @Override public boolean equals(Object other) {
307       if (!(other instanceof ParameterizedType)) {
308         return false;
309       }
310       ParameterizedType that = (ParameterizedType) other;
311       return getRawType().equals(that.getRawType())
312           && Objects.equal(getOwnerType(), that.getOwnerType())
313           && Arrays.equals(
314               getActualTypeArguments(), that.getActualTypeArguments());
315     }
316 
317     private static final long serialVersionUID = 0;
318   }
319 
320   private static final class TypeVariableImpl<D extends GenericDeclaration>
321       implements TypeVariable<D> {
322 
323     private final D genericDeclaration;
324     private final String name;
325     private final ImmutableList<Type> bounds;
326 
327     TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) {
328       disallowPrimitiveType(bounds, "bound for type variable");
329       this.genericDeclaration = checkNotNull(genericDeclaration);
330       this.name = checkNotNull(name);
331       this.bounds = ImmutableList.copyOf(bounds);
332     }
333 
334     @Override public Type[] getBounds() {
335       return toArray(bounds);
336     }
337 
338     @Override public D getGenericDeclaration() {
339       return genericDeclaration;
340     }
341 
342     @Override public String getName() {
343       return name;
344     }
345 
346     @Override public String toString() {
347       return name;
348     }
349 
350     @Override public int hashCode() {
351       return genericDeclaration.hashCode() ^ name.hashCode();
352     }
353 
354     @Override public boolean equals(Object obj) {
355       if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
356         // equal only to our TypeVariable implementation with identical bounds
357         if (obj instanceof TypeVariableImpl) {
358           TypeVariableImpl<?> that = (TypeVariableImpl<?>) obj;
359           return name.equals(that.getName())
360               && genericDeclaration.equals(that.getGenericDeclaration())
361               && bounds.equals(that.bounds);
362         }
363         return false;
364       } else {
365         // equal to any TypeVariable implementation regardless of bounds
366         if (obj instanceof TypeVariable) {
367           TypeVariable<?> that = (TypeVariable<?>) obj;
368           return name.equals(that.getName())
369               && genericDeclaration.equals(that.getGenericDeclaration());
370         }
371         return false;
372       }
373     }
374   }
375 
376   static final class WildcardTypeImpl implements WildcardType, Serializable {
377 
378     private final ImmutableList<Type> lowerBounds;
379     private final ImmutableList<Type> upperBounds;
380 
381     WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
382       disallowPrimitiveType(lowerBounds, "lower bound for wildcard");
383       disallowPrimitiveType(upperBounds, "upper bound for wildcard");
384       this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds);
385       this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds);
386     }
387 
388     @Override public Type[] getLowerBounds() {
389       return toArray(lowerBounds);
390     }
391 
392     @Override public Type[] getUpperBounds() {
393       return toArray(upperBounds);
394     }
395 
396     @Override public boolean equals(Object obj) {
397       if (obj instanceof WildcardType) {
398         WildcardType that = (WildcardType) obj;
399         return lowerBounds.equals(Arrays.asList(that.getLowerBounds()))
400             && upperBounds.equals(Arrays.asList(that.getUpperBounds()));
401       }
402       return false;
403     }
404 
405     @Override public int hashCode() {
406       return lowerBounds.hashCode() ^ upperBounds.hashCode();
407     }
408 
409     @Override public String toString() {
410       StringBuilder builder = new StringBuilder("?");
411       for (Type lowerBound : lowerBounds) {
412         builder.append(" super ").append(JavaVersion.CURRENT.typeName(lowerBound));
413       }
414       for (Type upperBound : filterUpperBounds(upperBounds)) {
415         builder.append(" extends ").append(JavaVersion.CURRENT.typeName(upperBound));
416       }
417       return builder.toString();
418     }
419 
420     private static final long serialVersionUID = 0;
421   }
422 
423   private static Type[] toArray(Collection<Type> types) {
424     return types.toArray(new Type[types.size()]);
425   }
426 
427   private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) {
428     return Iterables.filter(
429         bounds, Predicates.not(Predicates.<Type>equalTo(Object.class)));
430   }
431 
432   private static void disallowPrimitiveType(Type[] types, String usedAs) {
433     for (Type type : types) {
434       if (type instanceof Class) {
435         Class<?> cls = (Class<?>) type;
436         checkArgument(!cls.isPrimitive(),
437             "Primitive type '%s' used as %s", cls, usedAs);
438       }
439     }
440   }
441 
442   /** Returns the {@code Class} object of arrays with {@code componentType}. */
443   static Class<?> getArrayClass(Class<?> componentType) {
444     // TODO(user): This is not the most efficient way to handle generic
445     // arrays, but is there another way to extract the array class in a
446     // non-hacky way (i.e. using String value class names- "[L...")?
447     return Array.newInstance(componentType, 0).getClass();
448   }
449 
450   // TODO(benyu): Once we are on Java 8, delete this abstraction
451   enum JavaVersion {
452 
453     JAVA6 {
454       @Override GenericArrayType newArrayType(Type componentType) {
455         return new GenericArrayTypeImpl(componentType);
456       }
457       @Override Type usedInGenericType(Type type) {
458         checkNotNull(type);
459         if (type instanceof Class) {
460           Class<?> cls = (Class<?>) type;
461           if (cls.isArray()) {
462             return new GenericArrayTypeImpl(cls.getComponentType());
463           }
464         }
465         return type;
466       }
467     },
468     JAVA7 {
469       @Override Type newArrayType(Type componentType) {
470         if (componentType instanceof Class) {
471           return getArrayClass((Class<?>) componentType);
472         } else {
473           return new GenericArrayTypeImpl(componentType);
474         }
475       }
476       @Override Type usedInGenericType(Type type) {
477         return checkNotNull(type);
478       }
479     },
480     JAVA8 {
481       @Override Type newArrayType(Type componentType) {
482         return JAVA7.newArrayType(componentType);
483       }
484       @Override Type usedInGenericType(Type type) {
485         return JAVA7.usedInGenericType(type);
486       }
487       @Override String typeName(Type type) {
488         try {
489           Method getTypeName = Type.class.getMethod("getTypeName");
490           return (String) getTypeName.invoke(type);
491         } catch (NoSuchMethodException e) {
492           throw new AssertionError("Type.getTypeName should be available in Java 8");
493         } catch (InvocationTargetException e) {
494           throw new RuntimeException(e);
495         } catch (IllegalAccessException e) {
496           throw new RuntimeException(e);
497         }
498       }
499     }
500     ;
501 
502     static final JavaVersion CURRENT;
503     static {
504       if (AnnotatedElement.class.isAssignableFrom(TypeVariable.class)) {
505         CURRENT = JAVA8;
506       } else if (new TypeCapture<int[]>() {}.capture() instanceof Class) {
507         CURRENT = JAVA7;
508       } else {
509         CURRENT = JAVA6;
510       }
511     }
512 
513     abstract Type newArrayType(Type componentType);
514     abstract Type usedInGenericType(Type type);
515     String typeName(Type type) {
516       return Types.toString(type);
517     }
518 
519     final ImmutableList<Type> usedInGenericType(Type[] types) {
520       ImmutableList.Builder<Type> builder = ImmutableList.builder();
521       for (Type type : types) {
522         builder.add(usedInGenericType(type));
523       }
524       return builder.build();
525     }
526   }
527 
528   /**
529    * Per https://code.google.com/p/guava-libraries/issues/detail?id=1635,
530    * In JDK 1.7.0_51-b13, TypeVariableImpl.equals() is changed to no longer be equal to custom
531    * TypeVariable implementations. As a result, we need to make sure our TypeVariable implementation
532    * respects symmetry.
533    * Moreover, we don't want to reconstruct a native type variable <A> using our implementation
534    * unless some of its bounds have changed in resolution. This avoids creating unequal TypeVariable
535    * implementation unnecessarily. When the bounds do change, however, it's fine for the synthetic
536    * TypeVariable to be unequal to any native TypeVariable anyway.
537    */
538   static final class NativeTypeVariableEquals<X> {
539     static final boolean NATIVE_TYPE_VARIABLE_ONLY =
540         !NativeTypeVariableEquals.class.getTypeParameters()[0].equals(
541             newArtificialTypeVariable(NativeTypeVariableEquals.class, "X"));
542   }
543 
544   private Types() {}
545 }